# zennotac-backend/app/utils/filename_helper.py
from app.config.base_setting import UPLOAD_FOLDER, OCR_OUTPUT_FOLDER
from urllib.parse import unquote
from pathlib import Path
import os
from fastapi import HTTPException
from app.config.base_setting import backend_logger
from typing import Optional

def sanitize_filename(filename: str) -> str:
    """
    Decode percent-encoding and strip path components to prevent traversal attacks.
    """
    return os.path.basename(unquote(filename))

def ensure_valid_path(path: str | Path) -> str:
    """
    Ensure the given path is absolute and exists. Expands user (~) and resolves symlinks.
    """
    p = Path(path)
    return str(p.expanduser().resolve())

def resolve_file_path(filename: str, base_folder: str, create_dir: bool = True) -> str:
    """
    Sanitize filename, optionally create directory, and return a valid absolute file path.
    """
    safe_filename = sanitize_filename(filename)
    if create_dir:
        Path(base_folder).mkdir(parents=True, exist_ok=True)
    full_path = os.path.join(base_folder, safe_filename)
    return ensure_valid_path(full_path)

def get_upload_file_path(filename: str) -> str:
    """
    Resolve full path for an uploaded file.
    """
    return resolve_file_path(filename, UPLOAD_FOLDER)

def resolve_ocred_file_path(filename: str) -> str:
    """Sanitize and resolve full path to a ocred file location."""
    return resolve_file_path(filename, OCR_OUTPUT_FOLDER)

# def generate_ocred_output_path(input_path: str, suffix: str = "_cleaned") -> str:
#     """Generate a new output file path by adding suffix to input file stem."""
#     p = Path(input_path)
#     new_name = p.stem + f"{suffix}{p.suffix}"
#     return str(p.with_name(new_name))

def generate_ocred_output_path(input_path: str, suffix: str = "_cleaned", ext: Optional[str] = None) -> str:
    """
    Generate a new output file path by adding suffix to input stem.
    Optionally override the extension (e.g., .xlsx).
    """
    p = Path(input_path)
    new_name = p.stem + suffix + (ext if ext else p.suffix)
    return str(p.with_name(new_name))


def get_valid_file_location(filename: str, base_folder: str) -> str:
    """
    Sanitize and resolve a filename within a base folder.
    Raises 404 HTTPException if the resolved file doesn't exist.
    """
    file_location = resolve_file_path(filename, base_folder, create_dir=False)

    if not os.path.exists(file_location):
        backend_logger.warning(f"[FILE HELPER] File not found: {file_location}")
        raise HTTPException(status_code=404, detail="Ocred file not found.")

    return file_location

def safe_join(base, *paths):
    """
    Safely join one or more path components to the base directory to prevent path traversal.
    """
    final_path = os.path.abspath(os.path.join(base, *paths))
    base_path = os.path.abspath(base)

    if not final_path.startswith(base_path):
        raise ValueError("Attempted path traversal outside base directory.")

    return final_path



# # zennotac-backend/app/utils/filename_helper.py
# import os
# from pathlib import Path
# from urllib.parse import unquote
# from fastapi import HTTPException
# from app.config.base_setting import UPLOAD_FOLDER, OCR_OUTPUT_FOLDER, backend_logger

# def sanitize_filename(filename: str) -> str:
#     """
#     Decode percent-encoding (e.g. '%E4%BD%A0' → '你') and strip any path components.
#     Keeps Unicode characters intact.
#     """
#     decoded = unquote(filename)
#     return os.path.basename(decoded)

# def ensure_absolute(path: str) -> str:
#     """
#     Return an absolute path without resolving symlinks.
#     """
#     return os.path.abspath(str(path))

# def resolve_file_path(filename: str, base_folder: str, create_dir: bool = True) -> str:
#     """
#     Sanitize filename, ensure the base_folder exists (if create_dir),
#     and return an absolute path under base_folder.
#     """
#     safe_name = sanitize_filename(filename)
#     if create_dir:
#         Path(base_folder).mkdir(parents=True, exist_ok=True)
#     full_path = Path(base_folder) / safe_name
#     return ensure_absolute(full_path)

# def get_upload_file_path(filename: str) -> str:
#     """
#     Returns the absolute path for an upload, creating `app/uploads/` if needed.
#     """
#     return resolve_file_path(filename, UPLOAD_FOLDER, create_dir=True)

# def get_ocred_output_path(filename: str) -> str:
#     """
#     Returns the absolute path for ocred outputs, creating `app/ocred/` if needed.
#     """
#     return resolve_file_path(filename, OCR_OUTPUT_FOLDER, create_dir=True)

# def get_ocred_output_path(input_path: str, suffix: str = "_ocred") -> str:
#     """
#     Given any existing file path, append `suffix` to its stem (before the extension).
#     E.g. '/app/ocred/file.pdf' → '/app/ocred/file_ocred.pdf'
#     """
#     p = Path(input_path)
#     return str(Path(p.parent) / f"{p.stem}{suffix}{p.suffix}")

# def get_valid_file_location(filename: str, base_folder: str) -> str:
#     """
#     Resolve and return the absolute path for an existing file in base_folder.
#     Raises HTTPException(404) if the file isn’t found.
#     """
#     path = resolve_file_path(filename, base_folder, create_dir=False)
#     if not Path(path).is_file():
#         backend_logger.warning(f"[FILE HELPER] File not found: {path}")
#         raise HTTPException(status_code=404, detail="File not found.")
#     return path
